package ffte.core.streamed

import spinal.core._
import spinal.lib._

import ffte.types.VecComplex
import ffte.property.{getFFTIW,getFFTOW,FFTIOConfig}
import ffte.algorithm.FFTGen.{Method,combineFT}
import ffte.core.{fft,combine,streamedIO,getStreamedGroup}

/**
 * Base trait for streamed FFT components
 *
 * This trait provides the foundation for all streamed (pipelined) FFT implementations.
 * Streamed FFTs process data in a pipelined fashion with flow control, enabling
 * continuous data processing with high throughput and predictable latency.
 *
 * Architecture Characteristics:
 * - Pipelined processing: Multiple data samples in flight simultaneously
 * - Flow control: Ready/valid handshaking for backpressure support
 * - Fragment support: Handles burst data transfers with last signal
 * - Group processing: Processes multiple samples per clock cycle for efficiency
 *
 * Performance Benefits:
 * - High throughput: Continuous data processing capability
 * - Predictable latency: Fixed pipeline depth
 * - Backpressure support: Handles downstream congestion gracefully
 * - Scalable performance: Throughput scales with group size
 *
 * Resource Considerations:
 * - Higher resource usage than flat implementations
 * - Pipeline registers for timing optimization
 * - Memory for data buffering between stages
 * - DSP resources for complex arithmetic operations
 */
trait streamedComponents extends Component with fft {
    /**
     * Group size for parallel processing
     *
     * Retrieved from global configuration. Determines how many complex samples
     * are processed together in each clock cycle. Larger groups provide
     * higher throughput but require more resources.
     */
    val G = getStreamedGroup()

    /**
     * Streaming I/O interface
     *
     * Subclasses must implement this interface to provide the actual
     * input and output connections for the FFT component.
     */
    val io: streamedIO

    /**
     * Generate streaming I/O interface
     *
     * Creates a standardized streaming interface with the configured
     * group size and data widths. This provides consistent I/O
     * across all streamed FFT implementations.
     *
     * @return Streaming I/O bundle with flow control
     */
    def iogen = streamedIO(G, dW, oW)
}

/**
 * Combined streamed FFT components trait
 *
 * This trait provides common functionality for multi-stage streamed FFT implementations
 * that combine two smaller FFT operations. It builds on the combine trait to
 * handle hierarchical FFT decomposition in the streamed domain.
 *
 * Architecture Strategy:
 * - Both stages use streamed implementation for maximum throughput
 * - Hierarchical decomposition for large transforms
 * - Pipelined data flow between stages
 * - Consistent streaming interfaces throughout the hierarchy
 *
 * Scaling Distribution:
 * - First stage: Uses first_shift scaling bits
 * - Second stage: Uses second_shift scaling bits
 * - Intermediate precision managed to prevent overflow
 * - Width conversion between stages handled automatically
 *
 * @tparam T Method type for sub-FFT implementations
 */
trait combineStreamedComponents[T <: Method] extends streamedComponents with combine[T] {
    /**
     * First (inner) stage streamed FFT processor
     *
     * Creates a streamed FFT processor for the inner stage using the
     * allocated scaling bits. Both stages use streamed implementation
     * for consistent high-throughput performance.
     */
    val fg = new Streamed(first_shift)

    /**
     * Second (outer) stage streamed FFT processor
     *
     * Creates a streamed FFT processor for the outer stage using the
     * remaining scaling bits. Maintains architectural consistency
     * with the first stage.
     */
    val sg = new Streamed(second_shift)

    /** Standard streaming I/O interface */
    val io = iogen

    /**
     * First stage processing block
     *
     * Encapsulates the first stage FFT processor with appropriate
     * width configuration for the streamed domain.
     *
     * Width Configuration:
     * - Input width: dW (global input precision)
     * - Output width: interW (intermediate precision)
     * - Streaming interface with flow control
     */
    val f_block = FFTIOConfig(dW, interW) on new Area {
        /**
         * First stage streamed FFT processor
         *
         * Instantiates the streamed FFT for the first stage with
         * proper naming for debugging and identification.
         */
        val u = fg(first, "f")
        val delay = u.delay
    }
}

/**
 * Utility object for streamed FFT components
 *
 * Provides common utility functions for processing streamed complex data
 * in FFT implementations. These functions handle common transformations
 * and operations needed across different FFT architectures.
 */
object streamedComponents {
    /**
     * Complex conjugate operation for inverse FFT
     *
     * Implements the conjugate operation required for inverse FFT by swapping
     * the real and imaginary components of complex numbers in the streaming
     * domain. This is a common technique in fixed-point FFT implementations.
     *
     * Operation:
     * For complex number (a + bj), the conjugate is (b + aj)
     * This swap implements the conjugate twiddle factor handling
     *
     * @param x Input stream of complex vectors with fragment support
     * @return Stream with conjugated complex numbers
     */
    def inv(x: Stream[Fragment[VecComplex]]) = {
        val np = Fragment(x.dup)
        np.last := x.payload.last
        np.fragment := x.payload.fragment.swap
        x.translateWith(np)
    }

    /**
     * Resize operation for width conversion
     *
     * Resizes complex numbers in the streaming domain to match
     * required bit widths. This handles precision conversion
     * between different stages of FFT processing.
     *
     * Operation:
     * - Preserves last signal for burst handling
     * - Applies resize operation to complex vector
     * - Maintains streaming interface semantics
     *
     * @param x Input stream of complex vectors
     * @param dW Target data width in bits
     * @return Stream with resized complex numbers
     */
    def resize(x: Stream[Fragment[VecComplex]], dW: Int) = {
        val np = Fragment(x.dup)
        np.last := x.payload.last
        np.fragment := x.payload.fragment.resize(dW)
        x.translateWith(np)
    }
}


